<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 示例HTML -->
    <span id="demo">
        我是demo
    </span>
</body>
<script>
    /**
     * 什么是浏览器的重排重绘
     * 重排（Reflow）: 当页面的布局发生变化后，影响了元素的位置或者尺寸时，触发重排，这会导致整个渲染树的重新计算，涉及布局和几何属性的更新
     * 重绘（Redraw）：当某些元素外观的改变，但不影响它在文档中的位置，触发重绘，这意味着浏览器会重新绘制受影响元素的外观，但不会改变他们的布局。
    */

    /**
     * 重排和重绘的关系
     * 重绘不一定重排，重排一定会重绘
     * 这意味着当我们只改变元素的颜色，背景等外观属性，知会触发重绘从而不会引起整个页面布局的改变。
     * 但一旦改变了元素的位置或者尺寸的属性，将触发重排，从而导致触发重绘
     * 
     * 代价高昂的性能开销，需谨慎处理
     * 重排和重绘都会消耗大量的计算资源，特别时在频繁触发的情况下，会影响页面的性能和用户的体验，因此在前端开发中，需要谨慎处理操作，以减少不必要的重排和重绘。
    */

   /**
    * 重绘的常见操作
    * 修改颜色：修改元素的属性（color, background, border-color）
    * 调整边框的样式 border-style
    * 调整阴影效果 box-shadow
    * 文本修饰：对文本进行修饰，比如改变文本的装饰属性 text-decoration
   */

  /**
   * 重排的常见操作
   * 添加或者删除可见的DOM元素，
   * 改变元素的位置：修改元素的位置，比如改变定位属性position或者调整浮动float
   * 改变元素的尺寸：包括边距，填充，边框，宽度，高度等属性的修改，
   * 修改元素的内容：例如改变文字数量和图片大小
   * 改变文字的字体大小，
   * 改变浏览器的窗口尺寸，例如发生resize事件
   * 激活伪类CSS：hover
   * 查询某些属性或者调用计算方法：offsetWidth,offsetHeight,getComputedStyle
   * 
   * 重排影响范围
   * 全局范围：全局布局：从根节点html开始对整个渲染树进行重新布局
   * 局部范围：局部布局：对渲染树的某部队或者对某一个渲染对象进行重新布局
  */


 /**
  * 重排性能优化建议
  * 
 */
// 样式集中修改（合并样式修改）
// 示例JavaScript
    // const renderEle = document.getElementById('demo');
    // renderEle.style.color = 'red'; // 导致重绘
    // renderEle.style.background= '#ccc'; // 导致重绘
    // renderEle.style.padding = '15px 20px'; // 导致重排（重排会引起重绘）
    // 可以改为
    // 优化后的JavaScript
    // document.getElementById('demo').className = 'demo'; // 添加class 统一添加/修改样式
    // .demo {
    //     color: red;
    //     background: #ccc;
    //     padding: 15px 20px;
    // }
    /**
     * 样式集中修改：使用动态class
     * 动态添加class可以合并样式修改，从而减少重绘次数
    */

   /**
    * 将DOM离线：脱离文档流操作
    * 离线操作dom是指： 当对 dom 节点有较大改动的时候，我们先将元素脱离文档流，然后对元素进行操作，最后再把操作后的元素放回文档流
    * （1）改变 display 属性，临时将某个元素从文档流中脱离，然后再恢复
    * dom.style.display = 'none';
    * // 对 dom 进行操作
    * dom.style.display = 'block'
    * （2）通过 createDocumentFragment,创建文档片段，操作后一次性把文档片段添加到文档流中
    * const fragment = document.createDocumentFragment()
    * // 在 fragment 上进行一系列操作
    * document.getElementById('list').appendChild(fragment)
    * （3）通过在需要操作的节点上创建副本，然后在副本上进行操作，最后进行替换
    * const ul = document.getElementById('list')
      const clone = ul.cloneNode(true)
      // 对 clone 节点进行操作
      ul.parentNode.replaceChild(clone, ul)
    * （4）使用定位脱离文档流
    *
        const renderEle = document.getElementById('demo-one');
        renderEle.style.position = 'fixed'; // 导致重排（重排会引起重绘）
        renderEle.style.padding = '15px 20px'; // 导致重排（只有当前元素）
        renderEle.style.height = '60px'; // 导致重排（只有当前元素）
    （5）使用读写分离的优化策略
        在写入值的过程中，可以通过缓存的方式避免直接读取导致的重排，从而提高性能。
        // 示例JavaScript
        const offsetWidth = '100px';
        const renderEle = document.getElementById('demo');
        renderEle.style.offsetWidth = offsetWidth // 导致重绘(写入)
        const tempoOffsetWidth = renderEle.style.offsetWidth // 读取可能会导致重排
        以优化为：
        // 优化后的JavaScript
        const offsetWidth = '100px';
        const renderEle = document.getElementById('demo');
        renderEle.style.offsetWidth = offsetWidth // 导致重绘(写入)
        const tempoOffsetWidth = renderEle； // 避免直接读取offsetWidth
        通过读写分离的方式，将写入和读取操作分开，避免直接读取导致的性能损耗。
        这种优化策略适用于一些浏览器在获取属性时可能触发重排的情况，通过缓存写入的值，可以避免不必要的性能开销
    (6) 利用transform实现动画变化效果，去替代left top的变换
   */

  /**
   * 
   * 问： 将元素脱离文档流这一步是必须的吗？如果元素脱离文档流引起页面大面积重绘和重排，
   * 那不是更麻烦吗，还是说在在布局的时候就在需要脱离文档流的元素外面使用一个固定宽高位置的div包裹，避免整页重排？
   * 答： 如果不脱离文档流，对文档的每一步操作都有可能导致重绘或者重排，但是使用离线dom操作可以让操作完成之后只产生一次重排。
  */

 // 优化后的JavaScript
const offsetWidth = '100px';
const renderEle = document.getElementById('demo');
renderEle.style.offsetWidth = offsetWidth // 导致重绘(写入)
const tempoOffsetWidth = renderEle // 避免直接读取offsetWidth
console.log('tempoOffsetWidth', tempoOffsetWidth)
</script>
</html>